package com.vpx.server.framework.aop;

import com.github.pagehelper.util.SqlUtil;
import com.vpx.server.bean.enums.Status;
import com.vpx.server.bean.order.system.SysExceptionLogOrder;
import com.vpx.server.bean.order.system.SysOperateLogOrder;
import com.vpx.server.framework.base.BaseOrder;
import com.vpx.server.framework.base.ResultMsg;
import com.vpx.server.framework.exception.BizException;
import com.vpx.server.framework.utils.Reflections;
import com.vpx.server.service.system.SysExceptionLogService;
import com.vpx.server.service.system.SysOperateLogService;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

import java.lang.reflect.Method;

/**
 * @author xx
 * @title: vpx
 * @module 业务切面预处理器，业务基础骨架
 * @description:
 * @date
 */
@Aspect
@Configuration
public class ServiceSectionHandler {

    private final static Logger logger = LoggerFactory.getLogger(ServiceSectionHandler.class);
    private final static String DEFAULT_EXCEPTION_MESSAGE = "系统繁忙，请稍后尝试";
    private final static String DEFAULT_DUPLICATE_MESSAGE = "代号重复,请重新输入";
    private static final String DEFAULT_SUCCESS_MESSAGE = "业务处理成功";
    private static final String DEFAULT_SUCCESS_CODE = "1";
    private static final String DEFAULT_SYS_ERROR_CODE = "9999";
    private static final String DEFAULT_BIZ_ERROR_CODE = "0";
    private static final String MESSAGE_PROPERTY = "message";
    private static final String STATUS_PROPERTY = "status";
    private static final String CODE_PROPERTY = "code";
    @Autowired
    protected TransactionTemplate transactionTemplate;
    @Autowired
    private SysExceptionLogService sysExceptionLogService;
    @Autowired
    private SysOperateLogService sysOperateLogService;

    @Pointcut("@annotation( com.vpx.server.framework.aop.ServiceHandler)")
    public void servicePoint() {

    }

    /**
     * 业务预处理器
     *
     * @param proceedingJoinPoint
     */
    @Around("servicePoint()")
    public Object invoke(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        MethodSignature signature = (MethodSignature) proceedingJoinPoint.getSignature();
        Method method = signature.getMethod();
        ServiceHandler serviceHandler = method.getAnnotation(ServiceHandler.class);
        if (null == serviceHandler) {
            //为空时去获取实现类方法上的注解
            String methodName = proceedingJoinPoint.getSignature().getName();
            Class<?> classTarget = proceedingJoinPoint.getTarget().getClass();
            Class<?>[] par = ((MethodSignature) proceedingJoinPoint.getSignature()).getParameterTypes();
            Method implMethod = classTarget.getMethod(methodName, par);
            serviceHandler = implMethod.getAnnotation(ServiceHandler.class);
        }
        Object[] params = proceedingJoinPoint.getArgs();
        String memo = serviceHandler.memo();
        for (Object object : params) {
            logger.info("收到业务请求:{}，订单:{}。", memo, object);
            if (object instanceof BaseOrder) {
                //TODO 记录入参
            }
        }
        Class<?> returnType = method.getReturnType();
        boolean isBaseResult = ResultMsg.class.isAssignableFrom(returnType);
        Object result = null;
        try {
            // 业务实现
            if (serviceHandler.isTransaction()) {
                logger.info("[{}]业务处理开始[事务开启]", memo);
                AppBiz biz = new AppBiz(proceedingJoinPoint);
                result = transactionTemplate.execute(biz);
            } else {
                logger.info("[{}]业务处理开始[事务未开启]", memo);
                result = proceedingJoinPoint.proceed();
            }
            // 结果集解析
            if (result instanceof Exception) {
                throw (Throwable) result;
            } else {
                if (isBaseResult) {
                    Reflections.invokeSetter(result, STATUS_PROPERTY, Status.SUCCESS);
                    // 当提示消息为空时才设置
                    if (null == Reflections.invokeGetter(result, CODE_PROPERTY)) {
                        Reflections.invokeSetter(result, CODE_PROPERTY, DEFAULT_SUCCESS_CODE);
                    }
                    // 当提示消息为空时才设置
                    if (null == Reflections.invokeGetter(result, MESSAGE_PROPERTY)) {
                        Reflections.invokeSetter(result, MESSAGE_PROPERTY, DEFAULT_SUCCESS_MESSAGE);
                    }

                }
            }
            //记录操作日志
            SysOperateLogOrder operateLogOrder = new SysOperateLogOrder();
            operateLogOrder.setOperateName(memo);
            operateLogOrder.setOperatorResult("SUCCESS");
            operateLogOrder.setLogType(serviceHandler.type());
            operateLogOrder.setLogModule(serviceHandler.module());
            operateLogOrder.setSlotNum(serviceHandler.slotNum());
            sysOperateLogService.insert(operateLogOrder);
        }
        // 业务异常处理
        catch (com.vpx.server.framework.exception.BizException bizException) {
            result = invokeBizException(memo, bizException, isBaseResult, returnType, result);
        }
        // 解决未知的运行异常
        catch (Exception e) {
            result = invokeException(memo, e, isBaseResult, returnType, result);
        } finally {
            SqlUtil.clearLocalPage();
        }
        logger.info("[{}]业务请求处理完成，结果:{}", memo, result);
        return result;
    }

    /**
     * invokeBizException 异常
     *
     * @param memo
     * @param bizException
     * @param isBaseResult
     * @param returnType
     * @param result
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    private Object invokeBizException(String memo, BizException bizException, boolean isBaseResult, Class<?> returnType, Object result) throws InstantiationException, IllegalAccessException {
        logger.warn("[{}]业务处理发生错误:[{}]", memo, bizException.getMessage());
        //记录操作日志
        SysOperateLogOrder operateLogOrder = new SysOperateLogOrder();
        operateLogOrder.setOperateName(memo);
        operateLogOrder.setOperatorResult("FAIL");
        operateLogOrder.setFailDesc(bizException.getMessage());
        sysOperateLogService.insert(operateLogOrder);
        if (isBaseResult) {
            result = returnType.newInstance();
            Reflections.invokeSetter(result, STATUS_PROPERTY, Status.FAIL);
            Reflections.invokeSetter(result, CODE_PROPERTY, DEFAULT_BIZ_ERROR_CODE);
            Reflections.invokeSetter(result, MESSAGE_PROPERTY, bizException.getMessage());
        } else {
            throw bizException;
        }
        return result;
    }

    /**
     * 解决未知的运行异常
     *
     * @param memo
     * @param e
     * @param isBaseResult
     * @param returnType
     * @param result
     * @return
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    private Object invokeException(String memo, Exception e, boolean isBaseResult, Class<?> returnType, Object result) throws InstantiationException, IllegalAccessException {
        SysExceptionLogOrder sysExceptionLogOrder = new SysExceptionLogOrder();
        sysExceptionLogOrder.setOperateName(memo);
        sysExceptionLogOrder.setExceptionName(e.getClass().toString());
        sysExceptionLogOrder.setExceptionDetail(e.toString());
        sysExceptionLogService.insert(sysExceptionLogOrder);
        logger.warn("[{}]业务处理发生错误:[{}]", memo, e);
        if (isBaseResult) {
            result = returnType.newInstance();
            Reflections.invokeSetter(result, STATUS_PROPERTY, Status.FAIL);
            Reflections.invokeSetter(result, CODE_PROPERTY, DEFAULT_SYS_ERROR_CODE);
            Reflections.invokeSetter(result, MESSAGE_PROPERTY, DEFAULT_EXCEPTION_MESSAGE);
        } else {
            throw new BizException(DEFAULT_EXCEPTION_MESSAGE);
        }
        return result;
    }

    /**
     * 编程式事务
     */
    private class AppBiz implements TransactionCallback<Object> {
        private ProceedingJoinPoint proceedingJoinPoint;

        AppBiz(ProceedingJoinPoint proceedingJoinPoint) {
            this.proceedingJoinPoint = proceedingJoinPoint;
        }

        @Override
        public Object doInTransaction(TransactionStatus status) {
            try {
                //业务执行
                return proceedingJoinPoint.proceed();
            } catch (Throwable e) {
                status.setRollbackOnly();
                if (e instanceof BizException) {
                    logger.warn("系统内部发生业务异常：{}", e.getMessage());
                    throw (BizException) e;
                } else if (e instanceof DuplicateKeyException) {
                    logger.error("数据库唯一索引异常：", e);
                    String codeIndex = "code_index";
                    String factoryNumberIndex = "factory_number_index";
                    if (e.getMessage().contains(codeIndex)) {
                        throw new BizException(DEFAULT_DUPLICATE_MESSAGE);
                    } else if (e.getMessage().contains(factoryNumberIndex)) {
                        throw new BizException("设备出厂编号重复");
                    } else {
                        throw new BizException(DEFAULT_EXCEPTION_MESSAGE);
                    }
                } else {
                    return e;
                }
            }
        }
    }
}
